/**
  ******************************************************************************
  * @file        
  * @author      古么宁
  * @brief       unix domain socket 相关处理
  ******************************************************************************
  *
  * COPYRIGHT(c) 2019 GoodMorning
  *
  ******************************************************************************
  */

/* Includes -----------------------------------------------------------------*/
#include <stdio.h>
#include <stddef.h>

#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>  

#include "unixsock.h"


/* Private types ------------------------------------------------------------*/
/* Private macro ------------------------------------------------------------*/
/* Private variables --------------------------------------------------------*/
/* Global  variables --------------------------------------------------------*/

/// unix socket 文件存放文件夹
const char unsockdir[] = "/tmp";

/* Private function prototypes ----------------------------------------------*/
/* Gorgeous Split-line ------------------------------------------------------*/

/**
  * @author   古么宁
  * @brief    unix socket client 尝试连接 server 端。
  * @param    server : server 端名，会自动补全为 /tmp/server.sock 文件名，不可为空
  * @param    client : client 端名，会自动补全为 /tmp/client.sock 文件名，为空则不绑定
  * @return   成功返回连接 sock(>0) ，否则返回 -1
*/
int unix_socket_connect(const char * server , const char * client)
{
	struct sockaddr_un server_un = {0};
	int sockfd,len;

	if (!server) {
		return -1;
	}

	/* 创建一个 socket */
	sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
	if (sockfd < 0){  
		printf("cannot create unix socket to connect %s\r\n",server);
		return -1;
	}

	/* 如果需要绑定 client 源则进行绑定 */
	if (client) {
		struct sockaddr_un client_un = {0}; 
		len  = sprintf(client_un.sun_path,"%s/%s.sock",unsockdir,client);  
		len += offsetof(struct sockaddr_un,sun_path); 
		client_un.sun_family = AF_UNIX;   
		unlink(client_un.sun_path);  
		if (bind(sockfd, (struct sockaddr *)&client_un, len) < 0) {
			printf("bind (%s) error\r\n",client_un.sun_path);  
			close(sockfd);
			return -1 ;
		}
	}

	/* 连接 server 地址 */
	server_un.sun_family = AF_UNIX;  
	len = sprintf(server_un.sun_path,"%s/%s.sock",unsockdir,server);
	len += offsetof(struct sockaddr_un, sun_path) ;
	if (connect(sockfd, (struct sockaddr *)&server_un, len) < 0){
		close(sockfd);
		return -1 ;
	}

	return sockfd;
}


/**
  * @author   古么宁
  * @brief    unix socket 创建 server 端。
  * @note     创建成功则调用回调函数并把创建成功的 socket 传入。
  * @param    server      : server 端名，会自动补全为 /tmp/server.sock 文件名
  * @param    listensize  : 监听 server 端长度
  * @return   成功返回 0 ，否则返回其他
*/
int unix_socket_server(const char * server , int listensize)
{
	struct sockaddr_un server_un = {0}; 
	int len , servlisten;
	if (!server || listensize < 1) {
		return -1;
	}
	
	/* 创建本地 socket 连接 */
	servlisten = socket(AF_UNIX, SOCK_STREAM, 0) ; 
	if (servlisten < 0) { 
		printf("socket error\r\n");  
		return -1;
	}

	/* 初始化本地 socket ，绑定对应的本地文件路径 */
	server_un.sun_family = AF_UNIX;  
	len  = sprintf(server_un.sun_path,"%s/%s.sock",unsockdir,server);  
	len += offsetof(struct sockaddr_un, sun_path) ;
	unlink(server_un.sun_path); 
	if (bind(servlisten, (struct sockaddr *)&server_un,len) < 0) {  
		close(servlisten);
		printf("bind (%s) error\r\n",server_un.sun_path);  
		return -1;
	}

	/* 对本地 socket 进行监听，监听队列为 listensize */
	if (listen(servlisten,listensize) < 0) {  
		close(servlisten);
		printf("listen error\r\n");  
		return -1;
	}

	return servlisten;
}

